package net.fxug.hokuriku.chocolat.service
{
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.events.ServerSocketConnectEvent;
    import flash.net.ServerSocket;
    import flash.net.Socket;

    import net.fxug.hokuriku.chocolat.events.VoteEvent;
    import net.fxug.hokuriku.chocolat.model.VoteData;
    import net.fxug.hokuriku.chocolat.model.networkInterface.data.NetworkInterfaceData;
    import net.fxug.hokuriku.chocolat.model.tracer.Tracer;
    import net.fxug.hokuriku.chocolat.service.socket.constants.socketConstants;
    import net.fxug.hokuriku.chocolat.service.sonar.Sonar;
    import net.fxug.hokuriku.chocolat.service.sonar.constants.SonarModeConstants;

    /**
     *
     * @author mac
     */
    public class SocketServer implements IVoteWatcher
    {
        /** 自身のインターフェイスプロトコルを表現する文字列 */
        public static const WATCHER_TYPE:String = "lan";
        private var sonar:Sonar;

        private var serverSocket:ServerSocket;
        private var sockets:Vector.<Socket>;

        private var networkData:NetworkInterfaceData;

        /**
         *
         * @param inNetworkData
         * @throws IllegalOperationError
         */
        public function SocketServer(inNetworkData:NetworkInterfaceData)
        {
            if (inNetworkData is NetworkInterfaceData)
            {
                this.networkData = inNetworkData;
            }
            else
            {
                throw new IllegalOperationError("[Error] Illigal Constructor param error");
            }
            this.sockets = new Vector.<Socket>;
        }

        /**
         * @inheritDoc com.adobe.jp.smtimer.IVoteWatcher#getInterfaceName()
         */
        public function getInterfaceName():String
        {
            return WATCHER_TYPE;
        }

        /**
         *
         */
        public function start():void
        {
            this.sonar = new Sonar(this.networkData.address, SonarModeConstants.SERVER_MODE);

            try
            {
                this.sonar.recieve();

                    // TODO カスタム例外を作る？
            }
            catch (e:Error)
            {
                Tracer.t(e.message);
            }

            try
            {
                this.serverSocket = new ServerSocket();

            }
            catch (e:SecurityError)
            {
                // TODO エラー処理
            }
            this.serverSocket = new ServerSocket();

            Tracer.t("Server Socket Listen Start port = " + socketConstants.PORT + " / address = " + this.networkData.address);

            this.serverSocket.addEventListener(Event.CONNECT, this.serverSocketConnectEventHandler);

            this.serverSocket.bind(socketConstants.PORT, this.networkData.address);
            this.serverSocket.listen();

            //FIXME しもた、Adobeのプレリリースサイトを見れないので確認できん。
            // serverSocketが起動完了してリスン状態になったイベントで本来は以下を実行する必要がある。
            VoteManager.getInstance().onReady(this);
        }

        /**
         * リスン状態の場合にはtrueを返す。
         * @return サーバのリスン状態が有効な場合にtrue
         */
        public function isEnabled():Boolean
        {
            if (this.serverSocket == null)
            {
                return false;
            }
            return this.serverSocket.listening;
        }

        /**
         *
         */
        public function destory():void
        {
            //TODO 終了処理。サーバの停止など。
            //this.serverSocket.close();
        }

        private function serverSocketConnectEventHandler(event:ServerSocketConnectEvent):void
        {
            Tracer.t("SocketServer::serverSocketConnectEventHandler");

            var socket:Socket = Socket(event.socket);
            Tracer.t("    connected = " + socket.connected);
            if (!socket.connected)
            {
                Tracer.t("    socket connected = false error");
            }

            socket.addEventListener(ProgressEvent.SOCKET_DATA, this.socketProgressEventHandler);

            this.sockets.push(socket);
        }

        private function socketProgressEventHandler(event:ProgressEvent):void
        {
            Tracer.t("SocketServer::socketProgressEventHandler");
            var socket:Socket = event.target as Socket;
            var receivedStr:String = socket.readUTFBytes(socket.bytesAvailable)
            var receivedXML:XML;
            try
            {
                receivedXML = new XML(receivedStr);
            }
            catch (e:Error)
            {
                Tracer.t("XML Error" + e.message);
            }

            if (receivedXML.vote == VoteEvent.GOOD_EVENT || receivedXML.vote == VoteEvent.BAD_EVENT)
            {
                Tracer.t("    dispath voteEvent");

                receivedXML.message = String(receivedXML.message).replace("&gt;", "<");
                receivedXML.message = String(receivedXML.message).replace("&lt;", ">");

                Tracer.t("    vote = " + receivedXML.vote);
                Tracer.t("    message = " + receivedXML.message);

                var voteData:VoteData = new VoteData();
                voteData.id = receivedXML.user_name;
                voteData.text = receivedXML.message;
                voteData.voteEnabled = receivedXML.voteEnabled;
                voteData.route = SocketServer.WATCHER_TYPE;
                voteData.datetime = new Date();
                var voteEvent:VoteEvent = new VoteEvent(receivedXML.vote, [voteData]);
                VoteManager.getInstance().dispatchVote(voteEvent);
            }
        }
    }
}